#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <sys/mman.h>
#include <elf.h>


#define align_ptr(p, a)                                                   \
    (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

#include "symtab.h"

void print_shf_flags(int flag)
{
	
	#define V(n) { n, #n }

	struct Code {
	  int  value;
	  char name[13];
	} decode[] = {
	  V(SHF_WRITE),
	  V(SHF_ALLOC),
	  V(SHF_EXECINSTR),
	  V(SHF_MASKPROC),
	};
	#define BUFLEN (9+9+13+12+3+1+2)
	#define NUM_CODES sizeof(decode)/sizeof(decode[0])
	int i;
	char buf[BUFLEN];  /* declare the buffer */
	char* write = buf;    /* and a "write" pointer */
	for(i = 0; i < NUM_CODES; i++)
	{
		//if((flag & decode[i].value) == decode[i].value)
		//All bits included in this mask are reserved for processor-specific semantics.
		if(flag & decode[i].value)
		{
			if(write - buf > 0)
			{
				*write++ = '|';
			}
			else
			{
				*write++ = '(';
			}
			write += snprintf(write, BUFLEN - (write - buf), decode[i].name);
		}
	}
	if(write != buf)
	{
		*write++ = ')';
		*write = '\0';
		printf("%s", buf);
	}
	

	#undef V
	#undef NUM_CODES
}


int main(int argc, char* argv[])
{
	FILE* fp;
	Elf64_Ehdr* p64;
	Elf32_Ehdr* p32;
	Elf32_Shdr* psh32;
	Elf64_Shdr* psh64;
	Elf64_Shdr* psh64_it;
	Elf32_Sym* psym32;
	Elf64_Sym* psym64;
	Elf32_Phdr* pph32;
	Elf64_Phdr* pph64;
	int i;
	int flag_dump_symbols = 1;
	char* p_str_tab;

	if(argc < 2){
		printf("Usage: %s <ELF-FILE>\n",argv[0]);
		return 1;
	}
	fp = fopen(argv[1],"rb");
	if(!fp){
		printf("Error while opening this file: %s\n",argv[1]);
		return 1;
	}
	printf("Dumping ELF Header...\n\n\n");
	p32 = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	psh32 = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	fread(p32, sizeof(Elf64_Ehdr), 1, fp);
	printf("EI_MAG0~EI_MAG3:\t%c%c%c%c\n", p32->e_ident[0], p32->e_ident[1], p32->e_ident[2], p32->e_ident[3]);
	switch(p32->e_ident[4])
	{
		case 0:
			printf("EI_CLASS:\t%d => ELFCLASSNONE\n", p32->e_ident[4]);
			return 1;
		case 1:
			printf("EI_CLASS:\t%d => ELFCLASS32\n", p32->e_ident[4]);
			break;
		case 2:
			printf("EI_CLASS:\t%d => ELFCLASS64\n", p32->e_ident[4]);
			p64 = (Elf64_Ehdr*)p32;
			break;
	}
	switch(p32->e_ident[5])
	{
		case 0:
			printf("EI_DATA:\t%d => ELFDATANONE\n", p32->e_ident[5]);
			break;
		case 1:
			printf("EI_DATA:\t%d => ELFDATA2LSB(2’s complement values, with the least significant byte occupying the"
" lowest address.)\n", p32->e_ident[5]);
			break;
		case 2:
			printf("EI_DATA:\t%d => ELFDATA2MSB(2’s complement values, with the most significant byte occupying the"
" lowest address.)\n", p32->e_ident[5]);
			break;
	}
	switch(p32->e_ident[6])
	{
		case 0:
			printf("EI_VERSION:\t%d => EV_NONE\n", p32->e_ident[6]);
			break;
		case 1:
			printf("EI_VERSION:\t%d => EV_CURRENT\n", p32->e_ident[6]);
			break;
	}
	printf("EI_PAD:\t\t%d\n", p32->e_ident[7]);

	switch(p32->e_ident[4])
	{
		case 1:
			switch(p32->e_type)
			{
				case 0:
					printf("e_type:\t\t%d => ET_NONE\n", p32->e_type);
					break;
				case 1:
					printf("e_type:\t\t%d => ET_REL\n", p32->e_type);
					break;
				case 2:
					printf("e_type:\t\t%d => ET_EXEC\n", p32->e_type);
					break;
				case 3:
					printf("e_type:\t\t%d => ET_DYN\n", p32->e_type);
					break;
				case 4:
					printf("e_type:\t\t%d => ET_CORE\n", p32->e_type);
					break;
				default:
					printf("e_type:\t\t%d => Processor-specific OR Reserved\n", p32->e_type);
			}

			switch(p32->e_machine)
			{
				case 0:
					printf("e_machine:\t%d => EM_NONE\n", p32->e_machine);
					break;
				case 1:
					printf("e_machine:\t%d => EM_M32(AT&T WE 32100)\n", p32->e_machine);
					break;
				case 2:
					printf("e_machine:\t%d => EM_SPARC\n", p32->e_machine);
					break;
				case 3:
					printf("e_machine:\t%d => EM_386\n", p32->e_machine);
					break;
				case 4:
					printf("e_machine:\t%d => EM_68K(Motorola 68000)\n", p32->e_machine);
					break;
				case 5:
					printf("e_machine:\t%d => EM_88K(Motorola 88000)\n", p32->e_machine);
					break;
				case 7:
					printf("e_machine:\t%d => EM_860(Intel 80860)\n", p32->e_machine);
					break;
				case 8:
					printf("e_machine:\t%d => EM_MIPS(MIPS RS3000)\n", p32->e_machine);
					break;
				default:
					printf("e_machine:\t%d => Reserved\n", p32->e_machine);
			}
			switch(p32->e_version)
			{
				case 0:
					printf("e_version:\t%d => EV_NONE\n", p32->e_version);
					break;
				case 1:
					printf("e_version:\t%d => EV_CURRENT\n", p32->e_version);
					break;
			}
			break;
			printf("e_entry:\t%X\n", p32->e_entry);
			printf("e_phoff:\t%d\n", p32->e_phoff);
			printf("e_shoff:\t%d\n", p32->e_shoff);
			printf("e_flags:\t%X\n", p32->e_flags);
			printf("e_ehsize:\t%d\n", p32->e_ehsize);
			printf("e_phentsize:\t%d\n", p32->e_phentsize);
			printf("e_phnum:\t%d\n", p32->e_phnum);
			printf("e_shentsize:\t%d\n", p32->e_shentsize);
			printf("e_shnum:\t%d\n", p32->e_shnum);
			printf("e_shstrndx:\t%d\n", p32->e_shstrndx);
		case 2:
			switch(p64->e_type)
			{
				case 0:
					printf("e_type:\t\t%d => ET_NONE\n", p64->e_type);
					break;
				case 1:
					printf("e_type:\t\t%d => ET_REL\n", p64->e_type);
					break;
				case 2:
					printf("e_type:\t\t%d => ET_EXEC\n", p64->e_type);
					break;
				case 3:
					printf("e_type:\t\t%d => ET_DYN\n", p64->e_type);
					break;
				case 4:
					printf("e_type:\t\t%d => ET_CORE\n", p64->e_type);
					break;
				default:
					printf("e_type:\t\t%d => Processor-specific OR Reserved\n", p64->e_type);
			}

			switch(p64->e_machine)
			{
				case 0:
					printf("e_machine:\t%d => EM_NONE\n", p64->e_machine);
					break;
				case 1:
					printf("e_machine:\t%d => EM_M32(AT&T WE 32100)\n", p64->e_machine);
					break;
				case 2:
					printf("e_machine:\t%d => EM_SPARC\n", p64->e_machine);
					break;
				case 3:
					printf("e_machine:\t%d => EM_386\n", p64->e_machine);
					break;
				case 4:
					printf("e_machine:\t%d => EM_68K(Motorola 68000)\n", p64->e_machine);
					break;
				case 5:
					printf("e_machine:\t%d => EM_88K(Motorola 88000)\n", p64->e_machine);
					break;
				case 7:
					printf("e_machine:\t%d => EM_860(Intel 80860)\n", p64->e_machine);
					break;
				case 8:
					printf("e_machine:\t%d => EM_MIPS(MIPS RS3000)\n", p64->e_machine);
					break;
				default:
					printf("e_machine:\t%d => Reserved\n", p64->e_machine);
			}

			switch(p64->e_version)
			{
				case 0:
					printf("e_version:\t%d => EV_NONE\n", p64->e_version);
					break;
				case 1:
					printf("e_version:\t%d => EV_CURRENT\n", p64->e_version);
					break;
			}

			printf("e_entry:\t%lX\n", p64->e_entry);
			printf("e_phoff:\t%ld\n", p64->e_phoff);
			printf("e_shoff:\t%ld\n", p64->e_shoff);
			printf("e_flags:\t%X\n", p64->e_flags);
			printf("e_ehsize:\t%d\n", p64->e_ehsize);
			printf("e_phentsize:\t%d\n", p64->e_phentsize);
			printf("e_phnum:\t%d\n", p64->e_phnum);
			printf("e_shentsize:\t%d\n", p64->e_shentsize);
			printf("e_shnum:\t%d\n", p64->e_shnum);
			printf("e_shstrndx:\t%d\n", p64->e_shstrndx);
			
			fseek(fp,p64->e_shoff,SEEK_SET);
			psh64_it = psh64 = (Elf64_Shdr*)psh32;
			fread(psh64, sizeof(Elf64_Shdr) * p64->e_shnum, 1, fp);
			/*read section header string table*/
			p_str_tab = mmap(NULL, psh64[p64->e_shstrndx].sh_size, PROT_READ | PROT_WRITE,
                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
			fseek(fp, psh64[p64->e_shstrndx].sh_offset, SEEK_SET);
			fread(p_str_tab, psh64[p64->e_shstrndx].sh_size, 1, fp);
			printf("\n\n\nDumping ELF Section Header...\n\n\n");
			for(i = 0; i < p64->e_shnum; i++)
			{
				printf("Section Header Index:%d\n", i);
				switch(psh64_it->sh_name)
				{
					case 0:
						printf("sh_name:\t%d => SHN_UNDEF\n", psh64_it->sh_name);
						break;
					case 0xff00:
						printf("sh_name:\t%d => SHN_LORESERVE\n", psh64_it->sh_name);
						break;
					case 0xfff1:
						printf("sh_name:\t%d => SHN_ABS\n", psh64_it->sh_name);
						break;
					case 0xfff2:
						printf("sh_name:\t%d => SHN_COMMON\n", psh64_it->sh_name);
						break;
					case 0xffff:
						printf("sh_name:\t%d => SHN_HIRESERVE\n", psh64_it->sh_name);
						break;
					default:
						if(psh64_it->sh_name > 0xff00 && psh64_it->sh_name < 0xff1f)
						{
							printf("sh_name:\t%d => SHN_PROC\n", psh64->sh_name);
						}
						else
						{
							printf("sh_name:\t%s(%d)\n", &p_str_tab[psh64_it->sh_name], psh64_it->sh_name);
						}
				}
				switch(psh64_it->sh_type)
				{
					case 0:
						printf("sh_type:\t%d => SHT_NULL\n", psh64_it->sh_type);
						break;
					case 1:
						printf("sh_type:\t%d => SHT_PROGBITS\n", psh64_it->sh_type);
						break;
					case 2:
						printf("sh_type:\t%d => SHT_SYMTAB\n", psh64_it->sh_type);
						break;
					case 3:
						printf("sh_type:\t%d => SHT_STRTAB\n", psh64_it->sh_type);
						break;
					case 4:
						printf("sh_type:\t%d => SHT_RELA\n", psh64_it->sh_type);
						break;
					case 5:
						printf("sh_type:\t%d => SHT_HASH\n", psh64_it->sh_type);
						break;
					case 6:
						printf("sh_type:\t%d => SHT_DYNAMIC\n", psh64_it->sh_type);
						break;
					case 7:
						printf("sh_type:\t%d => SHT_NOTE\n", psh64_it->sh_type);
						break;
					case 8:
						printf("sh_type:\t%d => SHT_NOBITS\n", psh64_it->sh_type);
						break;
					case 9:
						printf("sh_type:\t%d => SHT_REL\n", psh64_it->sh_type);
						break;
					case 10:
						printf("sh_type:\t%d => SHT_SHLIB\n", psh64_it->sh_type);
					case 11:
						printf("sh_type:\t%d => SHT_DYNSYM\n", psh64_it->sh_type);
						break;
					default:
						if(psh64_it->sh_type >= 0x70000000 && psh64_it->sh_type < 0x7fffffff)
						{
							printf("sh_type:\t%d => SHN_PROC\n", psh64_it->sh_type);
						}
						else if(psh64_it->sh_type >= 0x80000000 && psh64_it->sh_type < 0xffffffff)
						{
							printf("sh_type:\t%d => SHN_USER\n", psh64_it->sh_type);
						}
						else
						{
							printf("sh_type:\t%d\n", psh64_it->sh_type);
						}
				}

				printf("sh_flags:\t%ld", psh64_it->sh_flags);
				print_shf_flags(psh64_it->sh_flags);
				printf("\n");
				printf("sh_addr:\t%lX\n", psh64_it->sh_addr);
				printf("sh_offset:\t%ld\n", psh64_it->sh_offset);
				printf("sh_size:\t%ld\n", psh64_it->sh_size);
				printf("sh_link:\t%d\n", psh64_it->sh_link);
				printf("sh_info:\t%d\n", psh64_it->sh_info);
				printf("sh_addralign:\t%lX\n", psh64_it->sh_addralign);
				printf("sh_entsize:\t%lX\n\n", psh64_it->sh_entsize);
				psh64_it++;
			}
			if(flag_dump_symbols)
			{
				dump_symtab_64(fp, psh64, p64->e_shnum);
			}
			munmap(p_str_tab, psh64[p64->e_shstrndx].sh_size);
	}


	
	fclose(fp);
	return 0;
}
